#include "public.h"
#include "util.h"
#include "http.h"
#include "global.h"
#include "getopt.h"

void GetMessageTypeFromProtoFile(
    const string& proto_filename,
    FileDescriptorProto* file_desc_proto)
{
    FILE* proto_file = fopen(proto_filename.c_str(), "r");
    {
        if (proto_file == NULL) {
            cout << "Cannot open .proto file: " << proto_filename;
        }

        FileInputStream proto_input_stream(fileno(proto_file));
        Tokenizer tokenizer(&proto_input_stream, NULL);
        Parser parser;
        if (!parser.Parse(&tokenizer, file_desc_proto)) {
            cout << "Cannot parse .proto file:" << proto_filename;
        }
    }
    fclose(proto_file);

    if (!file_desc_proto->has_name()) {
        file_desc_proto->set_name(proto_filename);
    }
}

int Test1()
{
    FileDescriptorProto pFileDescProto;
    GetMessageTypeFromProtoFile(g_stCmdArg.m_acProtoFilePath, &pFileDescProto);

    google::protobuf::DescriptorPool pool;
    const google::protobuf::FileDescriptor* pFileDesc = pool.BuildFile(pFileDescProto);
    if (pFileDesc == NULL) {
        cout << "Cannot get file descriptor from file descriptor" << pFileDescProto.DebugString();
        return -1;
    }

    char acData[10240] = { 0 };
    vReadFile(g_stCmdArg.m_acJsonFilePath, acData);

    cJSON *json = cJSON_Parse(acData);
    if (!json) {
        printf("Error before: [%s]\n", cJSON_GetErrorPtr());
        return -1;
    }
    cJSON *pRootItem = cJSON_GetArrayItem(json, 0);
    printf("json->name = %s, json->type = %d, pRootItem->string = %s\n", json->string, json->type, pRootItem->string);
    
    char * out = cJSON_Print(json);
    printf("%s\n", out);
    free(out);

    const google::protobuf::Descriptor* pMsgDesc = pFileDesc->FindMessageTypeByName(string(pRootItem->string));
    if (pMsgDesc == NULL) {
        cout << "Cannot get message descriptor of message: " << string(pRootItem->string);
        return -1;
    }
    cout << "pMsgDesc->name() = [" << pMsgDesc->name() << "]" << endl;
    cout << "pMsgDesc->full_name() = [" << pMsgDesc->full_name() << "]" << endl;
    cout << "pMsgDesc->index() = [" << pMsgDesc->index() << "]" << endl;
    cout << "pMsgDesc->field_count() = [" << pMsgDesc->field_count() << "]" << endl;
    cout << "pMsgDesc->nested_type_count() = [" << pMsgDesc->nested_type_count() << "]" << endl;
    cout << "pMsgDesc->enum_type_count() = [" << pMsgDesc->enum_type_count() << "]" << endl;

    google::protobuf::DynamicMessageFactory factory;
    const google::protobuf::Message* pCMsg = factory.GetPrototype(pMsgDesc);
    if (pCMsg == NULL) {
        cout << "Cannot create prototype message from message descriptor";
        return -1;
    }
    google::protobuf::Message* pMsg = pCMsg->New();
    if (pMsg == NULL) {
        cout << "Failed in pCMsg->New(); to create mutable message";
        return -1;
    }

    //cout << "1 pMsg->DebugString() = " << pMsg->DebugString() << endl;

    WalkJson(1, pRootItem, pMsgDesc, pMsg);

    //cout << "2 pMsg->DebugString() = [" << endl << pMsg->DebugString() << "]" << endl;

    char acResult[10240] = { 0 };
    pMsg->SerializePartialToArray(acResult, sizeof(acResult));
    cout << "acResult = [" << acResult << "]" << pMsg->ByteSize() << endl;

    SendHTTPPost(acResult, pMsg->ByteSize());

    cJSON_Delete(json);
    return 0;
}



int Test2()
{
    FileDescriptorProto pFileDescProto;
    GetMessageTypeFromProtoFile("E:\\Go\\GOPATH\\src\\jian\\GoBusinessDemo\\client\\client\\parseproto\\test.proto", &pFileDescProto);

    google::protobuf::DescriptorPool pool;
    const google::protobuf::FileDescriptor* pFileDesc = pool.BuildFile(pFileDescProto);
    if (pFileDesc == NULL) {
        cout << "Cannot get file descriptor from file descriptor" << pFileDescProto.DebugString();
        return -1;
    }

    //cout << "pFileDesc->DebugString() = [" << pFileDesc->DebugString() << "]" << endl << endl;
    cout << "pFileDesc->name() = [" << pFileDesc->name() << "]" << endl;
    cout << "pFileDesc->package() = [" << pFileDesc->package() << "]" << endl;
    cout << "pFileDesc->dependency_count() = [" << pFileDesc->dependency_count() << "]" << endl;
    cout << "pFileDesc->message_type_count() = [" << pFileDesc->message_type_count() << "]" << endl;
    cout << "pFileDesc->enum_type_count() = [" << pFileDesc->enum_type_count() << "]" << endl;
    cout << "pFileDesc->service_count() = [" << pFileDesc->service_count() << "]" << endl;
    cout << "pFileDesc->extension_count() = [" << pFileDesc->extension_count() << "]" << endl << endl << endl;

    for (int i = 0; i < pFileDesc->enum_type_count(); i++)
    {
        const EnumDescriptor* pEDesc = pFileDesc->enum_type(i);
        //cout << "pEDesc->DebugString() = [" << pEDesc->DebugString() << "]" << endl;
        cout << "pEDesc->name() = [" << pEDesc->name() << "]" << endl;
        cout << "pEDesc->full_name() = [" << pEDesc->full_name() << "]" << endl;
        cout << "pEDesc->index() = [" << pEDesc->index() << "]" << endl;
        cout << "pEDesc->value_count() = [" << pEDesc->value_count() << "]" << endl << endl;
    }

    for (int i = 0; i < pFileDesc->message_type_count(); i++)
    {
        const Descriptor* pDesc = pFileDesc->message_type(i);
        //cout << "pDesc->DebugString() = [" << pDesc->DebugString() << "]" << endl;
        cout << "pDesc->name() = [" << pDesc->name() << "]" << endl;
        cout << "pDesc->full_name() = [" << pDesc->full_name() << "]" << endl;
        cout << "pDesc->index() = [" << pDesc->index() << "]" << endl;
        cout << "pDesc->field_count() = [" << pDesc->field_count() << "]" << endl;
        cout << "pDesc->nested_type_count() = [" << pDesc->nested_type_count() << "]" << endl;
        cout << "pDesc->enum_type_count() = [" << pDesc->enum_type_count() << "]" << endl;

        for (int j = 0; j < pDesc->field_count(); j++)
        {
            const FieldDescriptor* pFieldDesc = pDesc->field(j);
            cout << "pFieldDesc->name() = [" << pFieldDesc->name() << "]" << endl;
        }

        cout << endl;
    }
    return 0;
}

void vHelp()
{
    printf("\n");
    printf("-i \"127.0.0.1\", web server ip address.\n");
    printf("-m 9090, web server port.\n");
    printf("-u \"/protoPostWrap\", URI.\n");
    printf("-t \"POST\", HTTP type, get, post.\n");
    printf("-p \"test.proto\", proto file path.\n");
    printf("-j \"test.json\", json file path.\n");
    printf("-h, help.\n");
    exit(0);
}

void vParseArg(int argc, char ** argv)
{
    g_stCmdArg.m_vClear();
    int c;
    while ((c = getopt(argc, argv, "i:m:u:t:p:j:h")) != -1)
    {
        int this_option_optind = optind ? optind : 1;
        switch (c)
        {
        case 'i':
        {
            strcpy(g_stCmdArg.m_acIP, optarg);
            break;
        }
        case 'm':
        {
            g_stCmdArg.m_iPort = atoi(optarg);
            break;
        }
        case 'u':
            strcpy(g_stCmdArg.m_acURI, optarg);
            printf("uuuuuuuuuuuuu = %s\n", optarg);
            break;
        case 't':
            strcpy(g_stCmdArg.m_acHTTPType, optarg);
            break;
        case 'p':
            strcpy(g_stCmdArg.m_acProtoFilePath, optarg);
            break;
        case 'j':
            strcpy(g_stCmdArg.m_acJsonFilePath, optarg);
            break;
        case 'h':
            vHelp();
            break;
        default:
            printf("Unknown option.\n");
            vHelp();
            break;
        }
    }

    if (optind < argc)
    {
        printf("non-option ARGV-elements: ");
        while (optind < argc)
        {
            printf("%s ", argv[optind++]);
        }
        printf("\n");
    }

    g_stCmdArg.m_vShow();
}

int main(int argc, char ** argv)
{
    vParseArg(argc, argv);
    Test1();
    return 0;
}
